package com.zzyl.nursing.service.impl;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.*;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zzyl.common.core.domain.entity.SysUser;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.IDCardUtils;
import com.zzyl.common.utils.StringUtils;

import com.zzyl.nursing.domain.*;
import com.zzyl.nursing.dto.CancelDto;
import com.zzyl.nursing.dto.DoPlanDto;
import com.zzyl.nursing.dto.UpdateJobDto;
import com.zzyl.nursing.mapper.CheckInMapper;
import com.zzyl.nursing.mapper.ElderMapper;
import com.zzyl.nursing.service.ICheckInConfigService;
import com.zzyl.nursing.service.ICheckInService;
import com.zzyl.nursing.service.INursingElderService;
import com.zzyl.nursing.vo.NursingNameVo;
import com.zzyl.nursing.vo.NursingTaskVO;
import com.zzyl.serve.domain.NursingLevel;
import com.zzyl.serve.domain.NursingProject;
import com.zzyl.serve.mapper.NursingPlanMapper;
import com.zzyl.nursing.service.INursingTaskService;
import com.zzyl.serve.mapper.NursingProjectPlanMapper;
import com.zzyl.serve.service.INursingLevelService;
import com.zzyl.serve.service.INursingPlanService;
import com.zzyl.serve.service.INursingProjectService;
import com.zzyl.serve.vo.NursingPlanVo;
import com.zzyl.serve.vo.NursingProjectPlanVo;
import com.zzyl.system.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.NursingTaskMapper;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * 护理任务Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-08-07
 */
@Service
public class NursingTaskServiceImpl extends ServiceImpl<NursingTaskMapper, NursingTask> implements INursingTaskService {
    @Autowired
    private NursingTaskMapper nursingTaskMapper;
    @Autowired
    private ElderMapper elderMapper;
    @Autowired
    private ICheckInService checkInService;
    @Autowired
    private NursingPlanMapper nursingPlanMapper;
    @Autowired
    private CheckInMapper checkInMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private NursingProjectPlanMapper nursingProjectPlanMapper;
    @Autowired
    private ICheckInConfigService checkInConfigService;

    @Autowired
    private INursingLevelService nursingLevelService;

    @Autowired
    private INursingPlanService nursingPlanService;

    @Autowired
    private INursingElderService nursingElderService;

    @Autowired
    private INursingProjectService nursingProjectService;
     /**
     * 查询护理任务
     *
     * @param id 护理任务主键
     * @return 护理任务
     */

    @Override
    public NursingTaskVO selectNursingTaskById(Long id)
    {
        NursingTaskVO nursingTaskVO= nursingTaskMapper.selectById1(id);
        if (ObjectUtil.isEmpty(nursingTaskVO)){
            return null;
        }
        Long elderId = nursingTaskVO.getElderId();

        String nursingId = nursingTaskVO.getNursingId();
        if (StringUtils.isEmpty(nursingId)){
            throw new BaseException("没有查询到护士id");
        }
        SysUser sysUser = sysUserMapper.selectUserById(Long.valueOf(nursingId));

        String nickName = sysUser.getNickName();
        ArrayList<String> userNameList = new ArrayList<>();
        userNameList.add(nickName);
        nursingTaskVO.setNursingName(userNameList);

        CheckIn checkIn = checkInMapper.selectElderByElderId(elderId);
        nursingTaskVO.setNursingLevelName(checkIn.getNursingLevelName());

        Elder elder = elderMapper.selectElderById(elderId);
        nursingTaskVO.setSex(elder.getSex());
        int ageByIdCard = IDCardUtils.getAgeByIdCard(elder.getIdCardNo());
        nursingTaskVO.setAge(ageByIdCard);

        return nursingTaskVO;
    }


    /**
     * 新增护理任务
     *
     * @param nursingTask 护理任务
     * @return 结果
     */
    @Override
    public int insertNursingTask(NursingTask nursingTask) {
        return save(nursingTask) ? 1 : 0;
    }

    /**
     * 修改护理任务
     *
     * @param nursingTask 护理任务
     * @return 结果
     */
    @Override
    public int updateNursingTask(NursingTask nursingTask) {
        return updateById(nursingTask) ? 1 : 0;
    }

    /**
     * 批量删除护理任务
     *
     * @param ids 需要删除的护理任务主键
     * @return 结果
     */
    @Override
    public int deleteNursingTaskByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除护理任务信息
     *
     * @param id 护理任务主键
     * @return 结果
     */
    @Override
    public int deleteNursingTaskById(Long id) {
        return removeById(id) ? 1 : 0;
    }


    public void addNursingTask_v2(Elder elder){
// 基础校验1：校验
        if (elder == null || elder.getId() == null) {
            log.error("老人不能为空");
            return;
        }

        // 基础校验2：获取入住信息
        CheckIn checkIn = checkInService.lambdaQuery().eq(CheckIn::getElderId, elder.getId()).one();
        if (ObjectUtil.isEmpty(checkIn)) {
            log.error("入住信息不能为空");
            return;
        }

        // 基础校验3：获取入住及入住配置信息
        CheckInConfig checkInConfig = checkInConfigService.lambdaQuery().eq(CheckInConfig::getCheckInId, checkIn.getId()).one();
        if (ObjectUtil.isEmpty(checkInConfig)) {
            log.error("入住配置不能为空");
            return;
        }

        // 基础校验4：护理等级
        NursingLevel nursingLevel = nursingLevelService.selectNursingLevelById(checkInConfig.getNursingLevelId());
        if (ObjectUtil.isEmpty(nursingLevel)) {
            log.error("护理等级不能为空");
            return;
        }

        // 基础校验5：护理计划
        NursingPlanVo nursingPlanVo = nursingPlanService.selectNursingPlanById(nursingLevel.getPlanId());
        if (ObjectUtil.isEmpty(nursingPlanVo)) {
            log.error("护理计划不能为空");
            return;
        }

        // 基础校验6：护理计划项目计划列表
        List<NursingProjectPlanVo> projectPlans = nursingPlanVo.getProjectPlans();
        if (CollUtil.isEmpty(projectPlans)) {
            log.error("护理计划项目计划列表不能为空");
            return;
        }

        // 基础校验7：查询老人对应的护理员列表
        List<NursingElder> nursingElderList = nursingElderService.lambdaQuery().eq(NursingElder::getElderId, elder.getId()).list();
        if (CollUtil.isEmpty(nursingElderList)) {
            log.error("护理人员不能为空");
            return;
        }

        List<Long> list = nursingElderList.stream().map(NursingElder::getNursingId).collect(Collectors.toList());
        // 多个id用逗号分隔，转换为字符串
        String nursingIds = StringUtils.join(list, ",");

        // 查询所有的护理项目
        List<NursingProject> projectList = nursingProjectService.list();
        // 转换为map key是id,value是name
        Map<Long, String> nursingProjectMap = projectList.stream().collect(Collectors.toMap(NursingProject::getId, NursingProject::getName));

        // 开始时间
        LocalDateTime finalStartTime = LocalDateTime.now();

        // 组装护理任务
        List<NursingTask> nursingTasks = new ArrayList<>();
        projectPlans.forEach(planVo -> {
            // 执行频次
            Integer executeFrequency = Integer.valueOf(String.valueOf(planVo.getExecuteFrequency()));
            // 执行时间
            String executeTime = planVo.getExecuteTime();
            LocalTime localTime = LocalTime.parse(executeTime);
            // 开始执行时间
            LocalDateTime firstExecutionTime = LocalDateTime.of(finalStartTime.toLocalDate(), localTime);
            // 计算相差天数
            // 修复：正确计算月末时间，避免无效日期如4月31日
            int year = finalStartTime.getYear();
            int month = checkInConfig.getFeeStartDate().getMonthValue();
            int lastDayOfMonth = java.time.YearMonth.of(year, month).lengthOfMonth();
            LocalDateTime monthEndTime = LocalDateTime.of(year, month, lastDayOfMonth, 23, 59);
            // 间隔天数
            //long diffDays = monthEndTime.toLocalDate().toEpochDay() - finalStartTime.toLocalDate().toEpochDay() + 1;
            long diffDays = Math.abs(LocalDateTimeUtil.between(finalStartTime, monthEndTime, ChronoUnit.DAYS));
            if ("0".equals(planVo.getExecuteCycle())) {
                // 日
                generateTaskByDay(firstExecutionTime, diffDays, nursingTasks, executeFrequency, elder, planVo, nursingIds, nursingProjectMap);
            } else if ("1".equals(planVo.getExecuteCycle())) {
                // 周
                generateTaskByWeek(firstExecutionTime, diffDays, nursingTasks, executeFrequency, elder, planVo, monthEndTime, nursingIds, nursingProjectMap);
            } else {
                // 月
                generateTaskByMonth(firstExecutionTime, monthEndTime, nursingTasks, executeFrequency, elder, planVo, nursingIds, nursingProjectMap);
            }
        });

        if (CollUtil.isEmpty(nursingTasks)) {
            return;
        }
        this.saveBatch(nursingTasks);

    }

    /**
     * 按月创建任务
     *
     * @param firstExecutionTime
     * @param monthEndTime
     * @param nursingTasks
     * @param executeFrequency
     * @param elder
     * @param v
     */
    private void generateTaskByMonth(LocalDateTime firstExecutionTime, LocalDateTime monthEndTime, List<NursingTask> nursingTasks, Integer executeFrequency, Elder elder, NursingProjectPlanVo v, String nursingIds, Map<Long, String> nursingProjectMap) {
        LocalDateTime executionTime = firstExecutionTime;
        Integer diffDay = (monthEndTime.plusSeconds(1).getDayOfMonth() - executionTime.getDayOfMonth()) / executeFrequency;
        for (int x = 0; x < executeFrequency; x++) {
            // 根据时间差和执行顺序计算每个任务的具体时间
            LocalDateTime seconds = executionTime.plusDays(diffDay * x);
            // 初始化护理任务对象
            NursingTask nursingTask = getNursingTask(elder, v, nursingIds, seconds, nursingProjectMap);
            // 将生成的任务添加到任务列表中
            nursingTasks.add(nursingTask);
        }
    }
    private void generateTaskByWeek(LocalDateTime firstExecutionTime, long diffDays, List<NursingTask> nursingTasks, Integer executeFrequency, Elder elder, NursingProjectPlanVo v, LocalDateTime monthEndTime, String nursingIds, Map<Long, String> nursingProjectMap) {
        int i;
        // 以7天为步长遍历差异天数，创建每周的任务
        for (i = 0; i < diffDays - 7; i = i + 7) {
            // 计算每周结束时间
            LocalDateTime dayEndTime = LocalDateTime
                    .of(firstExecutionTime.plusDays(i + 7).toLocalDate(), LocalTime.of(23, 59));
            // 计算本周的执行起始时间
            LocalDateTime executionTime = firstExecutionTime.plusDays(i);
            // 根据执行频率计算时间差，用于确定本周内各任务的时间点
            Integer diffDay = (dayEndTime.plusSeconds(1).getDayOfYear() - executionTime.getDayOfYear()) / executeFrequency;
            // 根据执行频率生成本周的任务
            for (int x = 0; x < executeFrequency; x++) {
                // 根据时间差和执行顺序计算每个任务的具体时间
                LocalDateTime seconds = executionTime.plusDays(diffDay * x);
                // 初始化护理任务对象
                NursingTask nursingTask = getNursingTask(elder, v, nursingIds, seconds, nursingProjectMap);
                // 将生成的任务添加到任务列表中
                nursingTasks.add(nursingTask);
            }
        }

        // 处理边界情况，当i超过diffDays-7且小于diffDays时
        if (i > diffDays - 7 && i < diffDays) {
            // 计算到达diffDays时的结束时间
            LocalDateTime dayEndTime = LocalDateTime.of(firstExecutionTime.plusDays(i + 7).toLocalDate(), LocalTime.of(23, 59));
            // 如果结束时间与开始时间年份不同，则返回，避免跨年任务
            if (ObjectUtil.notEqual(dayEndTime.getYear(), firstExecutionTime.getYear())) {
                return;
            }
            // 计算执行时间
            LocalDateTime executionTime = firstExecutionTime.plusDays(i);
            // 计算时间差，确定任务时间点
            Integer diffDay = (dayEndTime.plusSeconds(1).getDayOfYear() - executionTime.getDayOfYear()) / executeFrequency;
            // 根据执行频率生成任务，直到月末
            for (int x = 0; x < executeFrequency; x++) {
                // 计算每个任务的时间
                LocalDateTime seconds = executionTime.plusDays(diffDay * x);
                // 如果任务时间超过月末结束时间，则停止生成
                if (seconds.isAfter(monthEndTime)) {
                    break;
                }
                // 初始化护理任务对象
                NursingTask nursingTask = getNursingTask(elder, v, nursingIds, seconds, nursingProjectMap);
                // 将生成的任务添加到任务列表中
                nursingTasks.add(nursingTask);
            }
        }

    }
    private void generateTaskByDay(LocalDateTime firstExecutionTime, long diffDays, List<NursingTask> nursingTasks, Integer executeFrequency, Elder elder, NursingProjectPlanVo v, String nursingIds, Map<Long, String> nursingProjectMap) {
        // 遍历每一天，从首次执行时间开始，直到达到任务间隔天数
        for (int i = 0; i < diffDays; i++) {
            // 计算每一天的任务执行起始时间
            LocalDateTime executionTime = firstExecutionTime.plusDays(i);
            // 确定当天任务执行的结束时间（当天的23:59）
            LocalDateTime dayEndTime = LocalDateTime.of(executionTime.toLocalDate(), LocalTime.of(23, 59));
            // 计算当天内每次执行任务之间的小时差，用于确定执行时间点
            Integer diffHour = (dayEndTime.plusSeconds(1).getHour() - executionTime.getHour()) / executeFrequency;
            // 根据执行频率创建任务
            for (int x = 0; x < executeFrequency; x++) {
                // 计算每个任务的具体执行时间
                LocalDateTime seconds = executionTime.plusHours(diffHour * x);
                // 创建新的护理任务对象
                NursingTask nursingTask = getNursingTask(elder, v, nursingIds, seconds, nursingProjectMap);
                // 将生成的任务添加到任务列表中
                nursingTasks.add(nursingTask);
            }
        }
    }

    /**
     * 获取护理任务对象
     *
     * @param elder
     * @param v
     * @param nursingIds
     * @param seconds
     * @return
     */
    private NursingTask getNursingTask(Elder elder, NursingProjectPlanVo v, String nursingIds, LocalDateTime seconds, Map<Long, String> nursingProjectMap) {
        NursingTask nursingTask = new NursingTask();
        // 设置任务状态
        nursingTask.setStatus(1);
        // 设置护理员ID列表
        nursingTask.setNursingId(nursingIds);
        // 老人姓名
        nursingTask.setElderName(elder.getName());
        // 设置床位号
        nursingTask.setBedNumber(elder.getBedNumber());
        // 设置预估服务时间
        nursingTask.setEstimatedServerTime(seconds);
        // 设置项目ID
        nursingTask.setProjectId(Integer.valueOf(v.getProjectId()));
        // 设置老人ID
        nursingTask.setElderId(elder.getId());
        // 匹配护理项目
        nursingTask.setProjectName(nursingProjectMap.get(Long.valueOf(nursingTask.getProjectId()%9)));
        // 设置任务类型
        nursingTask.setTaskType(2);
        return nursingTask;
    }


// 以下为我写的---------------------------------------------------------------------------


    /**
     * 自动添加护理任务
     */
    @Override
    public void  addNursingTask(Elder elder){
        //涉及三个表 服务计划,服务等级,老人表//通过定时任务每日添加护理任务
        //1.先查出所有的老人 //"elderId": 2, //"elderName"："关羽", //bedNumber
//         List<Elder> elderList = elderMapper.selectAll();
//         if (CollUtil.isEmpty(elderList)){
//             return;
//         }
         //2.查询入住表,看出老人的护理等级 这个表这几个字段也有1.先查出所有的老人 //"elderId": 2, //"elderName"："关羽", //bedNumber
        List<CheckIn> checkIns = checkInService.list(Wrappers.<CheckIn>lambdaQuery().eq(CheckIn::getStatus,0));

        List<NursingTask> taskList = new ArrayList<>();
        if (CollUtil.isEmpty(checkIns)) {
            throw new BaseException("新增入住表为空");
        }

        //3.根据老人的护理等级再查出他们所有的护理计划
        checkIns.forEach(checkIn -> {

            String nursingLevelName = checkIn.getNursingLevelName();
            List<NursingTask> tasks = nursingPlanMapper.selectNursingPlanByLevel(nursingLevelName);
            tasks.forEach(task -> {
                task.setBedNumber(checkIn.getBedNumber());
                task.setElderId(checkIn.getElderId());
                task.setElderName(checkIn.getElderName());
                task.setStatus(1);

                List<NursingNameVo> nursingList= elderMapper.selectByNursingId(checkIn.getElderId());
                if (CollUtil.isEmpty(nursingList)){
                    throw new BaseException("查询数据为空");
                }
                // 过滤掉null元素
                List<NursingNameVo> validNursingList = nursingList.stream()
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList());

                nursingList.forEach(nursingNameVo -> {
                    // 检查nursingId是否为null
//                    StringBuilder a = new StringBuilder();
//                    if (nursingNameVo.getNursingId() != null) {
//                        for (Integer s : nursingNameVo.getNursingId()) {
//                            if (s != null) {
//                                a.append(s);
//                            }
//                        }
//                    }

                    String a = "";
                    for (Integer s : nursingNameVo.getNursingId()) {
                        a+=(s+",");
                    }
                    task.setNursingId(a.toString());
                    task.setNursingName(nursingNameVo.getNursingName());
                });
            });
            taskList.addAll(tasks);
        });

/*        //上面走的是新增入住的逻辑,下面是已经入住老人,他们没有新增入住表,其实不符合实际的逻辑
        //非首月：账单生成时生成任务；
        //若该老人已退住成功，则清空该老人待执行的任务：
        elderList.forEach(elder -> {
            Long elderId = elder.getId();
            String elderName = elder.getName();
            String bedNumber = elder.getBedNumber();

            List<NursingTask> tasks= nursingPlanMapper.selectNursingPlanByLevel(elderId);
            tasks.forEach(task->{
                task.setBedNumber(checkIn.getBedNumber());
                task.setElderId(checkIn.getElderId());
                task.setElderName(checkIn.getElderName());
                task.setStatus(1);
                //4.查询护士字段和名字,以及编号
                //先从老人护士表中查护士id,再在用户角色表中查用户id,再查姓名集合"nursingName
                List<NursingNameVo> nursingList= elderMapper.selectByNursingId(checkIn.getElderId());
                nursingList.forEach(nursingNameVo -> {
                    task.setNursingId(nursingNameVo.getNursingId());
                });
            });
            taskList.addAll(tasks);
        });*/
        //新增条件

        this.saveBatch(taskList);

        // 获取已退住老人ID列表
        List<Long> checkedOutElderIds = checkInService.list(Wrappers.<CheckIn>lambdaQuery()
                        .ne(CheckIn::getStatus, 0) // 状态不为入住
                        .select(CheckIn::getElderId))
                .stream()
                .map(CheckIn::getElderId)
                .collect(Collectors.toList());


        // 清空这些老人的待执行任务
        if (!checkedOutElderIds.isEmpty()) {
            remove(Wrappers.<NursingTask>lambdaQuery()
                    .in(NursingTask::getElderId, checkedOutElderIds)
                    .eq(NursingTask::getStatus, 1)); // 假设1为待执行状态
        }


    }



    /**
     * 查询护理任务列表
     *
     * @param nursingTask 护理任务
     * @return 护理任务
     */
    @Override
    public List<NursingTask> selectNursingTaskList(NursingTask nursingTask)
    {
        List<NursingTask> tasks = nursingTaskMapper.selectNursingTaskList(nursingTask);
        tasks.forEach(task -> {
            String nursingId = task.getNursingId();
            String[] split = nursingId.split(",");
            ArrayList<String> userNameList = new ArrayList<>();
            for (String s : split) {
                if (StringUtils.isNotEmpty(nursingId)){
                    SysUser sysUser = sysUserMapper.selectUserById(Long.valueOf(s));
                    String nickName = sysUser.getNickName();
                    userNameList.add(nickName);
                }
            }
            task.setNursingName(userNameList);

        });

        return tasks;
    }

    /**
     * 查询护理任务
     *
     * @param id 护理任务主键
     * @return 护理任务
     */


    /**
     * 2.2.3取消任务
     * @param cancelDto
     * @return
     */
    @Override
    public boolean updateByTaskId(CancelDto cancelDto) {

        int i = nursingTaskMapper.updateByTaskId(cancelDto);
        return i > 0;
    }

    @Override
    public void doPlan(DoPlanDto dto) {
        NursingTask nursingTask = new NursingTask();
        nursingTask.setMark(dto.getMark());
        nursingTask.setId(dto.getTaskId());
        nursingTask.setStatus(2);
        nursingTask.setEstimatedServerTime(dto.getEstimatedServerTime());
        nursingTask.setTaskImage(dto.getTaskImage());
        boolean b = updateById(nursingTask);
        System.out.println(b);
    }

    @Override
    public void updateTime(UpdateJobDto updateJobDto) {
        Integer taskId = updateJobDto.getTaskId();
        LocalDateTime estimatedServerTime = updateJobDto.getEstimatedServerTime();
        nursingTaskMapper.updateTimeByTaskId(taskId, estimatedServerTime);
    }
}
